package com.sh.data.engine.domain.normalization.modeldesign.dimfact.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sh.data.engine.common.constants.HttpStatus;
import com.sh.data.engine.common.constants.code.BizCodes;
import com.sh.data.engine.common.exception.BusinessException;
import com.sh.data.engine.common.util.ServletUtils;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.model.domain.BusMatrixDomain;
import com.sh.data.engine.domain.normalization.modeldesign.busmatrix.service.BusMatrixService;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.model.domain.GetTableNameDomain;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.model.domain.ModelBindWarnDomain;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.model.param.DeleteDimFactParam;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.model.param.SaveDimFactParam;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.model.param.SaveNewDimFactParam;
import com.sh.data.engine.domain.normalization.modeldesign.dimfact.service.DimFactService;
import com.sh.data.engine.domain.normalization.modeldesign.model.model.domain.ModelDomain;
import com.sh.data.engine.domain.normalization.modeldesign.model.service.ModelService;
import com.sh.data.engine.repository.dao.normalization.modeldesign.busmatrix.entity.MatrixEntity;
import com.sh.data.engine.repository.dao.normalization.modeldesign.dimfact.DimFactMapper;
import com.sh.data.engine.repository.dao.normalization.modeldesign.dimfact.entity.DimFactEntity;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author : changkong
 * @date : 2022/1/12
 */
@Slf4j
@Service
public class DimFactServiceImpl extends ServiceImpl<DimFactMapper, DimFactEntity>
    implements DimFactService {

    @Lazy
    @Autowired
    private BusMatrixService busMatrixService;

    @Lazy
    @Autowired
    private ModelService modelService;

    /**
     * 删除行
     *
     * @return
     */
    @Override
    public BusMatrixDomain deleteFactRow(DeleteDimFactParam deleteDimFactParam) {
        return internalDeleteDimFact(deleteDimFactParam);
    }

    /**
     * 删除列
     *
     * @return
     */
    @Override
    public BusMatrixDomain deleteDimCol(DeleteDimFactParam deleteDimFactParam) {
        return internalDeleteDimFact(deleteDimFactParam);
    }

    /**
     * 新增行 可能需要扩容matrix 插入行带有index, 默认在当前行上面新增
     *
     * @return
     */
    @Override
    public BusMatrixDomain saveNewFactRow(SaveNewDimFactParam saveNewDimFactParam) {
        return internalSaveNewDimFact(saveNewDimFactParam, "fact");
    }

    /**
     * 删除行列内部方法
     *
     * @param deleteDimFactParam
     * @return
     */
    private BusMatrixDomain internalDeleteDimFact(DeleteDimFactParam deleteDimFactParam) {
        Long themeId = deleteDimFactParam.getThemeId();
        // 删除节点不需要更新index
        boolean deleteResult =
            this.update(
                new LambdaUpdateWrapper<DimFactEntity>()
                    .eq(DimFactEntity::getId, deleteDimFactParam.getCurId())
                    .set(DimFactEntity::getRowState, 0));
        if (!deleteResult) {
            log.error("删除节点失败: {}", deleteDimFactParam.getCurId());
            throw new BusinessException(BizCodes.DELETE_DIMFACT_ERROR, HttpStatus.BAD_REQUEST);
        }
        // 拉取全量factEntity
        Pair<List<DimFactEntity>, List<DimFactEntity>> pair = fullFetchDimFactsAndPartByType(themeId);
        List<DimFactEntity> factLists = pair.getFirst();
        List<DimFactEntity> dimLists = pair.getSecond();
        log.info("sort end {}", System.currentTimeMillis());
        // 查factId和dimId对应的matrix
        List<Long> factIds = factLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        List<Long> dimIds = dimLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        // 全量拉取符合条件的item
        List<MatrixEntity> matrixInDb =
            busMatrixService.getBusMatrixEntityByFactIdsAndDimIds(factIds, dimIds);
        return busMatrixService.buildApiMatrix(factLists, dimLists, matrixInDb);
    }

    /**
     * 新增行列内部方法
     *
     * @param saveNewDimFactParam
     * @param type
     * @return
     */
    @Transactional(rollbackFor = Exception.class, transactionManager = "engineTransactionManager")
    public BusMatrixDomain internalSaveNewDimFact(
        SaveNewDimFactParam saveNewDimFactParam, String type) {
        Long themeId = saveNewDimFactParam.getThemeId();
        var projectId = ServletUtils.getProjectId();
        DimFactEntity insertEntity =
            this.convertDimFactEntity(saveNewDimFactParam, null, type);
        // 批量更新当前节点index后的所有节点
        boolean incResult = this.incMultiIndex(type, themeId, insertEntity.getPageIndex());
        if (!incResult) {
            log.error("check the sort index");
            throw new IllegalStateException("状态异常");
        }
        boolean save = this.save(insertEntity);
        if (!save) {
            throw new BusinessException(BizCodes.INSERT_DIMFACT_ERROR, HttpStatus.BAD_REQUEST);
        }
        // 拉取全量factEntity
        Pair<List<DimFactEntity>, List<DimFactEntity>> pair = fullFetchDimFactsAndPartByType(themeId);
        List<DimFactEntity> factLists = pair.getFirst();
        List<DimFactEntity> dimLists = pair.getSecond();
        log.info("sort end {}", System.currentTimeMillis());
        // 查factId和dimId对应的matrix
        List<Long> factIds = factLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        List<Long> dimIds = dimLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        // 全量拉取符合条件的item
        List<MatrixEntity> matrixInDb =
            busMatrixService.getBusMatrixEntityByFactIdsAndDimIds(factIds, dimIds);
        return busMatrixService.rebuildMatrix(matrixInDb, factLists, dimLists, insertEntity);
    }

    /**
     * 新增列 可能需要扩容matrix
     *
     * @param saveNewDimFactParam
     * @return
     */
    @Override
    public BusMatrixDomain saveNewDimCol(SaveNewDimFactParam saveNewDimFactParam) {
        return internalSaveNewDimFact(saveNewDimFactParam, "dim");
    }

    /**
     * 全量拉取主题下的fact和col
     *
     * @param themeId
     * @return
     */
    @Override
    public Pair<List<DimFactEntity>, List<DimFactEntity>> fullFetchDimFactsAndPartByType(
        Long themeId) {
        List<DimFactEntity> dimColAndFactRowByThemeId = this.getDimColAndFactRowByThemeId(themeId);
        List<DimFactEntity> factLists = new ArrayList<>();
        List<DimFactEntity> dimLists = new ArrayList<>();
        for (DimFactEntity item : dimColAndFactRowByThemeId) {
            if ("fact".equals(item.getType())) {
                factLists.add(item);
            } else if ("dim".equals(item.getType())) {
                dimLists.add(item);
            } else {
                log.error("check id:{}的状态", item.getId());
                throw new IllegalStateException("状态异常");
            }
        }
        return Pair.of(factLists, dimLists);
    }

    /**
     * 提供原始的行/列数据, 数据还需要排序后构建matrix
     *
     * @param themeId
     * @return
     */
    @Override
    public List<DimFactEntity> getDimColAndFactRowByThemeId(Long themeId) {
        List<DimFactEntity> dimColAndFactRowByThemeId =
            this.list(
                new LambdaQueryWrapper<DimFactEntity>()
                    .eq(DimFactEntity::getThemeId, themeId)
                    .eq(DimFactEntity::getRowState, 1)
                    .orderByAsc(DimFactEntity::getPageIndex));
        return dimColAndFactRowByThemeId;
    }

    /**
     * 获取绑定的表名,以及是否允许修改
     *
     * @param projectId 项目id
     * @param type      fact | dim
     * @param id        行|列的id
     * @return
     */
    @Override
    public GetTableNameDomain getTableNameById(Long projectId, String type, Long id) {
        DimFactEntity dimFactEntity = this.getById(id);
        if (null == dimFactEntity) {
            log.error("检查参数: {}", id);
            throw new BusinessException(BizCodes.DIMFACT_NOT_FOUND_ERROR, HttpStatus.BAD_REQUEST);
        }
        GetTableNameDomain getTableNameListDomain = new GetTableNameDomain();
        getTableNameListDomain.setThemeId(dimFactEntity.getThemeId());
        Long modelId = dimFactEntity.getTblPropId();
        if (Objects.isNull(modelId)) {
            // 没有关联模型
            getTableNameListDomain.setRefModelId(null);
            getTableNameListDomain.setLogicRel(false);
            return getTableNameListDomain;
        }
        // 有关联模型 通过modelId查表名
        ModelDomain dbModel = modelService.getSimpifyModelById(ServletUtils.getProjectId(), modelId);
        // 检查tblPropId是否合法
        if (null == dbModel || Objects.isNull(dbModel.getId())) {
            log.error("检查dimfact {} 关联模型状态 {}", id, dbModel.getId());
            throw new BusinessException(BizCodes.MODEL_NOT_ERROR, HttpStatus.BAD_REQUEST);
        }
        getTableNameListDomain.setRefModelId(dbModel.getId());
        // 查是否有关联字段
        Boolean rowColBindField = busMatrixService.isRowColBindField(type, id);
        getTableNameListDomain.setLogicRel(rowColBindField);
        return getTableNameListDomain;
    }

    /**
     * 更新维度列,事实行
     *
     * <p>如果修改的时候变更了主题,要进行矩阵的新增
     *
     * @param saveDimFactParam
     * @return
     */
    @Override
    public BusMatrixDomain saveDimFact(SaveDimFactParam saveDimFactParam) {
        // 检查当前的fact或者dim是否有关联字段
        var projectId = ServletUtils.getProjectId();
        GetTableNameDomain tableNameById =
            getTableNameById(
                projectId, saveDimFactParam.getType(), saveDimFactParam.getItemId());
        if (null == tableNameById) {
            throw new BusinessException(BizCodes.DIMFACT_NOT_FOUND_ERROR, HttpStatus.BAD_REQUEST);
        }
        if (!Objects.isNull(tableNameById.getRefModelId())) {
            // 如果有关联表, 禁止切换主题
            if (!Objects.equals(saveDimFactParam.getThemeId(), tableNameById.getThemeId())) {
                log.error("当前已设置关联字段,禁止切换主题");
                throw new BusinessException(BizCodes.THEME_BINDING_ALREADY_EXISTS_ERROR, HttpStatus.BAD_REQUEST);
            }
            // 如果存在字段逻辑绑定, 并且用户想要切换关联表, 抛异常
            if (tableNameById.isLogicRel()
                && !Objects.equals(saveDimFactParam.getTableId(), tableNameById.getRefModelId())) {
                log.error("当前已设置关联字段,禁止修改关联表");
                throw new BusinessException(BizCodes.REL_TABLE_BINDING_ALREADY_EXISTS_ERROR, HttpStatus.BAD_REQUEST);
            }
        }
        // 切换主题
        if (!Objects.equals(saveDimFactParam.getThemeId(), tableNameById.getThemeId())) {
            // 移动过去默认第一
            boolean incResult =
                this.incMultiIndex(saveDimFactParam.getType(), saveDimFactParam.getThemeId(), 0);
            if (!incResult) {
                log.error("check the sort index");
                throw new BusinessException(BizCodes.CHANGE_THEME_ERROR, HttpStatus.INTERNAL_SERVER_ERROR);
            }
            DimFactEntity dimFactEntity = new DimFactEntity();
            dimFactEntity.setId(saveDimFactParam.getItemId());
            dimFactEntity.setName(saveDimFactParam.getItemName());
            dimFactEntity.setThemeId(saveDimFactParam.getThemeId());
            dimFactEntity.setTblPropId(saveDimFactParam.getTableId());
            dimFactEntity.setType(saveDimFactParam.getType());
            dimFactEntity.setPageIndex(0);
            this.updateById(dimFactEntity);
            // 变更前的主题下的矩阵,减少
            busMatrixService.deleteMatrixItems(saveDimFactParam.getItemId(), saveDimFactParam.getType());
            // 变更后的主题下的矩阵,增加
            // 拉取全量factEntity
            Pair<List<DimFactEntity>, List<DimFactEntity>> pair =
                fullFetchDimFactsAndPartByType(saveDimFactParam.getThemeId());
            List<DimFactEntity> factLists = pair.getFirst();
            List<DimFactEntity> dimLists = pair.getSecond();
            // 查factId和dimId对应的matrix
            List<Long> factIds =
                factLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
            List<Long> dimIds = dimLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
            // 全量拉取符合条件的item
            List<MatrixEntity> matrixInDb =
                busMatrixService.getBusMatrixEntityByFactIdsAndDimIds(factIds, dimIds);
            return busMatrixService.rebuildMatrix(matrixInDb, factLists, dimLists, dimFactEntity);
        }

        // 只更改了其他字段, 更新dimFact表就可以, 但是前端需要全量数据进行渲染, 所以要再组装一遍数据
        DimFactEntity dimFactEntity = new DimFactEntity();
        dimFactEntity.setId(saveDimFactParam.getItemId());
        dimFactEntity.setName(saveDimFactParam.getItemName());
        dimFactEntity.setThemeId(saveDimFactParam.getThemeId());
        dimFactEntity.setTblPropId(saveDimFactParam.getTableId());
        dimFactEntity.setDescribe(saveDimFactParam.getItemDesc());
        this.updateById(dimFactEntity);

        // 拉取全量factEntity
        Pair<List<DimFactEntity>, List<DimFactEntity>> pair =
            fullFetchDimFactsAndPartByType(tableNameById.getThemeId());
        List<DimFactEntity> factLists = pair.getFirst();
        List<DimFactEntity> dimLists = pair.getSecond();

        List<Long> factIds = factLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        List<Long> dimIds = dimLists.stream().map(DimFactEntity::getId).collect(Collectors.toList());
        List<MatrixEntity> sourceMatrix =
            busMatrixService.getBusMatrixEntityByFactIdsAndDimIds(factIds, dimIds);
        // 构建展示数据并返回
        return busMatrixService.buildApiMatrix(factLists, dimLists, sourceMatrix);
    }

    /**
     * 模型被绑定到事实行,维度列的名称
     *
     * @param tblPropId
     * @return
     */
    @Override
    public ModelBindWarnDomain getBindDimFact(Long tblPropId) {
        DimFactEntity dimFactByModelId = getByTblPropId(tblPropId);
        if (null == dimFactByModelId) {
            return null;
        }
        ModelBindWarnDomain modelBindWarnDomain = new ModelBindWarnDomain();
        modelBindWarnDomain.setName(dimFactByModelId.getName());
        modelBindWarnDomain.setType(Objects.equals(dimFactByModelId.getType(), "dim") ? "维度" : "事实");
        return modelBindWarnDomain;
    }

    @Override
    public DimFactEntity getByModelId(Long modelId) {
        return this.getOne(
            new LambdaQueryWrapper<DimFactEntity>()
                .eq(DimFactEntity::getTblPropId, modelId)
                .eq(DimFactEntity::getRowState, 1));
    }

    @Override
    public DimFactEntity getByPrimaryKey(Long id) {
        return this.getById(id);
    }

    /**
     * @param tblPropId
     * @return
     */
    private DimFactEntity getByTblPropId(Long tblPropId) {
        return this.getOne(
            new LambdaQueryWrapper<DimFactEntity>()
                .eq(DimFactEntity::getTblPropId, tblPropId)
                .eq(DimFactEntity::getRowState, 1));
    }

    /**
     * 转换参数到entity
     *
     * @param saveNewDimFactParam
     * @param projectId
     * @param type
     * @return
     */
    private DimFactEntity convertDimFactEntity(
        SaveNewDimFactParam saveNewDimFactParam, Long projectId, String type) {
        DimFactEntity dimFactEntity = new DimFactEntity();
        dimFactEntity.setProjectId(projectId);
        dimFactEntity.setName(saveNewDimFactParam.getItemName());
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(saveNewDimFactParam.getItemDesc())) {
            dimFactEntity.setDescribe(saveNewDimFactParam.getItemDesc());
        }
        dimFactEntity.setRowState(1);
        dimFactEntity.setType(type);
        dimFactEntity.setPageIndex(saveNewDimFactParam.getCurIndex());
        dimFactEntity.setThemeId(saveNewDimFactParam.getThemeId());
        dimFactEntity.setTblPropId(saveNewDimFactParam.getTblPropId());
        return dimFactEntity;
    }

    /**
     * 插入新元素, 增加后续元素的排序
     *
     * @param type
     * @param themeId
     * @param index   >=index的元素index++
     * @return
     */
    private boolean incMultiIndex(String type, Long themeId, Integer index) {
        if (CollectionUtils.isNotEmpty(
            this.list(
                new LambdaQueryWrapper<DimFactEntity>()
                    .eq(DimFactEntity::getThemeId, themeId)
                    .eq(DimFactEntity::getType, type)
                    .eq(DimFactEntity::getRowState, 1)))) {
            return this.update(
                new LambdaUpdateWrapper<DimFactEntity>()
                    .eq(DimFactEntity::getThemeId, themeId)
                    .eq(DimFactEntity::getType, type)
                    .eq(DimFactEntity::getRowState, 1)
                    .ge(DimFactEntity::getPageIndex, index)
                    .setSql("page_index = page_index + 1"));
        }
        return true;
    }
}
